from abc import ABC, abstractmethod
import math
import tkinter as tk

import numpy as np


class Shape(ABC):
    def __init__(self, color="#000000", auto_fill=None,fill=None, is_filled=None, line_width=1, transform=None):
        """
        图形基类,定义通用属性和方法
        :param color:颜色值,默认黑色
        :param line_width:线宽,默认一个像素
        :param transform:仿射变换矩阵，格式为 [a, b, c, d, e, f]
        """
        self.fill = fill if fill else color  # 默认填充色与边框色相同
        self.color = color
        self.is_filled = is_filled if is_filled is not None else False
        self.line_width = line_width
        self.selected = False
        self.auto_fill = auto_fill
        # 仿射变换矩阵默认使用单位矩阵（无变换）
        self.transform_matrix = transform if transform else [1, 0, 0, 1, 0, 0]

    @abstractmethod
    def draw(self, canvas):
        pass

    @abstractmethod
    def get_bounding_box(self):
        pass

    def to_dict(self):
        # print("I'm shape")
        """通用序列化方法"""
        return {
            "type": self.__class__.__name__,
            "is_filled": self.is_filled,
            "color": self.color,
            "line_width": self.line_width,
            "transform": self.transform_matrix,
            "fill": self.fill,
            "auto_fill": self.auto_fill
        }

    @classmethod
    def from_dict(cls, data):
        """
        工厂方法反序列化
        功能：根据字典数据创建对应的具体图形类实例

        :param cls: 类对象（自动传入）
        :param data: 包含图形数据的字典，必须包含"type"字段表示具体子类名称
        :return: 具体图形子类的实例

        实现原理：
        1. 获取当前类的所有子类映射
        2. 根据数据类型字段找到对应的子类
        3. 调用子类的from_dict方法创建实例
        """

        # 创建子类名称到子类对象的映射字典
        # 遍历cls的所有子类，以类名为键，类对象为值
        subclasses = {sc.__name__: sc for sc in cls.__subclasses__()}

        # 根据数据中的"type"字段获取对应的子类
        # 并调用该子类的from_dict方法反序列化数据
        return subclasses[data["type"]].from_dict(data)

    def _validate_color(self, color):
        """统一颜色格式验证方法 """
        if isinstance(color, tuple) and len(color) == 3:
            # 处理RGB元组格式
            if all(0 <= c <= 255 for c in color):
                return "#{:02x}{:02x}{:02x}".format(*color)
            elif all(0 <= c <= 1 for c in color):
                return "#{:02x}{:02x}{:02x}".format(*(int(c * 255) for c in color))
        elif isinstance(color, str):
            # 处理预定义颜色名称和十六进制格式
            if color.startswith("#") and len(color) in (4, 7, 9):
                return color
            # elif color in tk.color_names:  # 系统预定义颜色
            #     return color
        return self.fill  # 默认使用边框色

    def get_fill_color(self):
        return self.fill if self.fill else self.color

    def move(self, dx, dy):
        """平移变换：直接修改变换矩阵的平移分量"""
        self.transform_matrix[4] += dx  # e分量控制X轴平移
        self.transform_matrix[5] += dy  # f分量控制Y轴平移（Tkinter坐标系Y轴向下）

    def rotate(self, angle_degrees):
        """旋转变换：生成新的变换矩阵"""
        rad = math.radians(angle_degrees)
        cos_a = math.cos(rad)
        sin_a = math.sin(rad)
        # 矩阵乘法实现旋转（保留原有平移分量）
        new_matrix = [
            self.transform_matrix[0] * cos_a - self.transform_matrix[1] * sin_a,
            self.transform_matrix[0] * sin_a + self.transform_matrix[1] * cos_a,
            self.transform_matrix[2] * cos_a - self.transform_matrix[3] * sin_a,
            self.transform_matrix[3] * cos_a + self.transform_matrix[2] * sin_a,
            self.transform_matrix[4],# 保留原有的x平移
            self.transform_matrix[5] # 保留原有的y平移
        ]
        self.transform_matrix = new_matrix

    def _apply_transform(self, point):
        """
        应用仿射变换到坐标点
        功能：将局部坐标系的点转换为经过变换后的屏幕坐标系点

        :param point: 元组(x,y)，表示局部坐标系中的点
        :return: 元组(x,y)，表示变换后的屏幕坐标

        变换公式（简化版）：
        x' = a * x + e
        y' = d * y + f

        其中变换矩阵为：
        [ a, b, c,
          d, e, f ]
        """

        # X坐标变换：
        # 1. 应用X轴缩放(transform_matrix[0])
        # 2. 加上X轴平移量(transform_matrix[4])
        x = point[0] * self.transform_matrix[0] + self.transform_matrix[4]

        # Y坐标变换：
        # 1. 应用Y轴缩放(transform_matrix[3])
        # 2. 加上Y轴平移量(transform_matrix[5])
        y = point[1] * self.transform_matrix[3] + self.transform_matrix[5]

        # 返回变换后的坐标
        return (x, y)

    def _get_inverse_matrix(self):
        """计算变换矩阵的逆矩阵"""
        a, b, c, d, e, f = self.transform_matrix
        det = a * d - b * c
        if det == 0:
            return [1, 0, 0, 1, -e, -f]  # 退化情况处理
        return [
            d/det, -b/det,
            -c/det, a/det,
            (b*f - d*e)/det, (c*e - a*f)/det
        ]


"""
$圆$
"""
class Circle(Shape):
    def __init__(self, center_x, center_y, radius, **kwargs):
        super().__init__(**kwargs)
        self.x = center_x
        self.y = center_y
        self.center = (center_x, center_y)
        self.radius = radius
        self.points = set()

    def to_dict(self):
        data = super().to_dict()
        data.update({
            "center": self.center,  # (x,y)元组
            "radius": self.radius
        })
        return data

    @classmethod
    def from_dict(cls, data):
        return cls(
            data["center"][0],  # center_x
            data["center"][1],  # center_y
            data["radius"],
            is_filled=data.get("is_filled", False),
            color=data["color"],
            line_width=data["line_width"],
            transform=data["transform"],
            fill=data.get("fill"),
            auto_fill=data.get("auto_fill", False)
        )

    def draw(self, canvas):
        x, y = self._apply_transform(self.center)

        # 动态计算缩放因子
        scale_x = self.transform_matrix[0]
        scale_y = self.transform_matrix[3]

        # 修改后的半径计算
        scaled_radius_x = self.radius * self.transform_matrix[0]
        scaled_radius_y = self.radius * self.transform_matrix[3]

        # 动态调整线宽
        scale_factor = max(scale_x, scale_y)
        width = int(self.line_width * scale_factor)
        if self.line_width <= 2:
            # print(f"in Circle,auto_fill:{self.auto_fill}")
            if self.auto_fill or self.is_filled:
                self.is_filled = True
                self.points = self.midpoint_circle(
                    canvas, x, y, self.radius,
                    color=self.color,
                    fill=self.get_fill_color(),
                    line_width=self.line_width)
            else:
                self.points = self.midpoint_circle(
                    canvas, x, y, self.radius,
                    color=self.color,
                    line_width=self.line_width)
        else:
            # 使用Tkinter原生方法绘制粗线圆（性能更优）
            if self.auto_fill or self.is_filled:
                self.is_filled = True
                canvas.create_oval(
                    x - scaled_radius_x, y - scaled_radius_y,
                    x + scaled_radius_x, y + scaled_radius_y,
                    outline=self.color,
                    fill=self.get_fill_color(),  # 新增填充色
                    width=int(self.line_width * max(scale_x, scale_y))  # 线宽同步缩放
                )
            else:
                canvas.create_oval(
                    x - scaled_radius_x, y - scaled_radius_y,
                    x + scaled_radius_x, y + scaled_radius_y,
                    outline=self.color,
                    width=int(self.line_width * max(scale_x, scale_y))  # 线宽同步缩放
                )
        """检测到选择功能的时候绘制一个框框"""
        if self.selected:
            # 绘制选择框
            x1, y1, x2, y2 = self.get_bounding_box()
            canvas.create_rectangle(x1 - 5, y1 - 5, x2 + 5, y2 + 5,
                                    outline='red', dash=(4, 2))

            # 绘制控制点
            handle_size = 8
            canvas.create_oval(x2 - handle_size // 2, y2 - handle_size // 2,
                               x2 + handle_size // 2, y2 + handle_size // 2,
                               fill='red')

    def _draw_selection_handle(self, canvas):
        bbox = self.get_bounding_box()
        handle_size = 8
        canvas.create_rectangle(
            bbox[2] - handle_size // 2, bbox[3] - handle_size // 2,
            bbox[2] + handle_size // 2, bbox[3] + handle_size // 2,
            fill='red', outline='black'
        )

    def get_bounding_box(self):
        x, y = self._apply_transform(self.center)
        return (x - self.radius, y - self.radius,
                x + self.radius, y + self.radius)


    # def _apply_transform(self, point):
    #     x = point[0] * self.transform_matrix[0] + self.transform_matrix[4]
    #     y = point[1] * self.transform_matrix[3] + self.transform_matrix[5]  # 移除错误的反转符号
    #     return (x, y)

    def contains(self, x, y):
        # 转换到局部坐标系（考虑仿射变换）
        local_x = (x - self.transform_matrix[4]) / self.transform_matrix[0]
        local_y = (y - self.transform_matrix[5]) / self.transform_matrix[3]
        dx = local_x - self.center[0]
        dy = local_y - self.center[1]
        return dx ** 2 + dy ** 2 <= self.radius ** 2

    def resize(self, dx, dy,canvas):
        x, y, radius = self.get_properties()
        self.radius = max(1, radius + dx)  # 假设dx用于调整半径，忽略dy
        self.draw(canvas)

    def get_properties(self):
        return self.x, self.y, self.radius

    @staticmethod
    def midpoint_circle(canvas, center_x, center_y, radius,  ** kwargs):
        """
        中点画圆法,并且填充
        :param canvas:目标画布对象
        :param center_x:圆心x
        :param center_y:圆心y
        :param radius:半径
        :param kwargs:其他参数
        :return:圆的所有点
        """
        fill_color = kwargs.get('fill', '')
        outline_color = kwargs.get('color', 'black')
        line_width = kwargs.get('line_width', 1)

        # 处理有效半径
        effective_radius = max(1, int(abs(radius))) # 确保半径至少为1的正整数
        points = set()# 存储圆上所有点
        x, y, d = 0, effective_radius, 1 - effective_radius# 初始化算法参数
        max_iterations = effective_radius * 2 + 10  # 安全计数器防止无限循环
        iteration = 0

        # 生成边界点
        while x <= y and iteration < max_iterations:
            # 计算8个对称点
            sym_points = [
                (center_x + x, center_y + y), (center_x - x, center_y + y),
                (center_x + x, center_y - y), (center_x - x, center_y - y),
                (center_x + y, center_y + x), (center_x - y, center_y + x),
                (center_x + y, center_y - x), (center_x - y, center_y - x)
            ]

            points.update(sym_points)# 使用集合存储点，自动去重

            # 更新决策参数
            if d < 0:
                # 选择E点（向东移动）
                # 更新公式：d_new = d + (2x + 3)
                d += 2 * x + 3
            else:
                # 选择SE点（向东南移动）
                # 更新公式：d_new = d + (2(x - y) + 5)
                d += 2 * (x - y) + 5
                y -= 1
            x += 1
            iteration += 1 # 安全计数器递增

        # 填充
        if fill_color:
            # 使用字典存储每行的最小和最大x坐标
            # 键是y坐标，值是包含min_x和max_x的字典
            scan_lines = {} # 存储每行的最小/最大x坐标

            # 遍历所有边界点，计算每行的扫描线范围
            for px, py in points:
                if py not in scan_lines:
                    # 如果是该y坐标的第一个点，初始化范围
                    scan_lines[py] = {'min_x': px, 'max_x': px}
                else:
                    # 更新该y坐标的最小和最大x值
                    scan_lines[py]['min_x'] = min(scan_lines[py]['min_x'], px)
                    scan_lines[py]['max_x'] = max(scan_lines[py]['max_x'], px)

            # 准备填充线的坐标序列
            fill_lines = []
            # 遍历每行扫描线数据
            for y_scan in scan_lines:
                x_start = scan_lines[y_scan]['min_x']# 行起始x坐标
                x_end = scan_lines[y_scan]['max_x']# 行结束x坐标
                # 将线段坐标添加到列表（格式：x1,y1,x2,y2）
                fill_lines.extend([x_start, y_scan, x_end, y_scan])

            # 如果有需要填充的线段
            if fill_lines:
                # 批量创建水平填充线（优化性能）
                canvas.create_line(
                    *fill_lines,    # 解包所有线段坐标
                   fill=fill_color,
                   width=0,         # 线宽为0表示实心填充
                   tag="fill_area"
                )

        # 绘制边界点
        for px, py in points:
            canvas.create_rectangle(
                px - 1, py - 1, px + 1, py + 1,
                outline=outline_color,
                fill=outline_color,
                width=0
            )
        return points





    # @staticmethod
    # def midpoint_circle(canvas, center_x, center_y, radius,  ** kwargs):
    #     """
    #
    #     :param canvas:
    #     :param center_x:
    #     :param center_y:
    #     :param radius:
    #     :param kwargs:
    #     :return:
    #     """
    #     fill_color = kwargs.get('fill', '')
    #     outline_color = kwargs.get('color', 'black')
    #     auto_fill = kwargs.get('auto_fill',False)
    #
    #
    #     line_width = kwargs.get('line_width', 1)
    #     expanded_radius = radius + line_width // 2
    #     # 算法核心逻辑生成离散点
    #     x, y, d = 0, expanded_radius, 1 - expanded_radius
    #     points = set()
    #
    #     while x <= y:
    #         # 生成8个对称点
    #         sym_points = [
    #             (center_x + x, center_y + y), (center_x - x, center_y + y),
    #             (center_x + x, center_y - y), (center_x - x, center_y - y),
    #             (center_x + y, center_y + x), (center_x - y, center_y + x),
    #             (center_x + y, center_y - x), (center_x - y, center_y - x)
    #         ]
    #         points.update(sym_points)
    #         # 更新决策参数
    #         if d < 0:
    #             d += 2 * x + 3
    #         else:
    #             d += 2 * (x - y) + 5
    #             y -= 1
    #         x += 1
    #     points = points
    #     # 调用静态填充方法
    #     if auto_fill:
    #         """canvas, center_x, center_y,
    #             points, fill_color, outline_color"""
    #         scanline_fill(
    #             canvas=canvas,
    #             center_x=center_x,
    #             center_y=center_y,
    #             boundary_points=points,
    #             fill_color=fill_color,
    #             outline_color=outline_color
    #         )
    #     else:
    #         # 批量绘制点
    #         for px, py in points:
    #             canvas.create_rectangle(
    #                 px - 1, py - 1, px + 1, py + 1,  # 放大绘制尺寸
    #                 outline=kwargs.get('color'),
    #                 fill=kwargs.get('color'),
    #                 width=0  # 消除边框
    #             )
    #     return points

"""
$直线$
"""
class Line(Shape):
    def __init__(self, start_x, start_y, end_x, end_y, ** kwargs):
        super().__init__(**kwargs)
        self.start = (start_x, start_y)
        self.end = (end_x, end_y)

    def to_dict(self):
        data = super().to_dict()
        data.update({
            "start": self.start,  # (x,y)
            "end": self.end
        })
        return data

    @classmethod
    def from_dict(cls, data):
        return cls(
            start_x=data["start"][0],
            start_y=data["start"][1],
            end_x=data["end"][0],
            end_y=data["end"][1],
            color=data["color"],
            line_width=data["line_width"],
            transform=data["transform"],
            fill=data.get("fill"),
            auto_fill=data.get("auto_fill", False)
        )

    def draw(self, canvas):
        p0 = self._apply_transform(self.start)
        p1 = self._apply_transform(self.end)

        # fill替代color参数
        canvas.create_line(*p0, *p1,
                           fill=self.color,  #  正确参数名
                           width=self.line_width)  #  线宽参数

        # 中点算法参数传递
        if self.line_width <= 2:
            self.midpoint_line(canvas, *p0, *p1,
                               fill=self.color,  #  替换-color
                               width=self.line_width)

    # 在Line类中修正变换方法
    def _apply_transform(self, point):
        """完整仿射变换（包含旋转）"""
        x = point[0] * self.transform_matrix[0] + \
            point[1] * self.transform_matrix[1] + \
            self.transform_matrix[4]
        y = point[0] * self.transform_matrix[2] + \
            point[1] * self.transform_matrix[3] + \
            self.transform_matrix[5]
        return (int(x), int(y))

    # 修改Line类的移动方法（移除变换矩阵干扰）
    def move(self, dx, dy):
        """直接操作原始坐标（忽略变换矩阵）"""
        self.start = (self.start[0] + dx, self.start[1] + dy)
        self.end = (self.end[0] + dx, self.end[1] + dy)

    def contains(self, x, y):
        # 增加线段碰撞检测
        p0 = self._apply_transform(self.start)
        p1 = self._apply_transform(self.end)

        # 线段方程检测（像素级精度）
        line_threshold = max(5, self.line_width * 2)
        distance = self._distance_to_line((x, y), p0, p1)
        return distance < line_threshold

    def _distance_to_line(self, point, start, end):
        """计算点到直线的几何距离"""
        x, y = point
        x1, y1 = start
        x2, y2 = end
        numerator = abs((y2 - y1) * x - (x2 - x1) * y + x2 * y1 - y2 * x1)
        denominator = math.sqrt((y2 - y1) ** 2 + (x2 - x1) ** 2)
        return numerator / denominator if denominator != 0 else 0

    # def contains(self, x, y):
    #     p0 = self._apply_transform(self.start)
    #     p1 = self._apply_transform(self.end)
    #
    #     # 增大端点检测范围（15像素半径）
    #     start_dist = math.hypot(x - p0[0], y - p0[1])
    #     end_dist = math.hypot(x - p1[0], y - p1[1])
    #     threshold = 15  # 原为5+线宽
    #
    #     if start_dist < threshold:
    #         return 'start_handle'
    #     elif end_dist < threshold:
    #         return 'end_handle'
    #     return False


    def get_bounding_box(self):
        # 返回格式：(min_x, min_y, max_x, max_y)
        p0 = self._apply_transform(self.start)
        p1 = self._apply_transform(self.end)
        return (min(p0[0], p1[0]), min(p0[1], p1[1]),
                max(p0[0], p1[0]), max(p0[1], p1[1]))


    @staticmethod
    def midpoint_line(canvas, x0, y0, x1, y1,  ** kwargs):
        """

        :param canvas:
        :param x0:起点x坐标
        :param y0:起点y坐标
        :param x1:终点x坐标
        :param y1:终点y坐标
        :param kwargs:
        :return:
        """
        points = []# 存储直线上的所有点坐标
        # 计算x和y方向的差值（绝对值）
        dx = abs(x1 - x0)
        dy = abs(y1 - y0)
        steep = dy > dx  # 判断斜率是否大于1 True表示斜率>1

        # 统一坐标系处理（将斜率>1的线转换为斜率<1的情况处理）
        if steep:
            # 交换x和y坐标（相当于关于y=x对称）
            x0, y0 = y0, x0
            x1, y1 = y1, x1
            # 同时交换dx和dy
            dx, dy = dy, dx
        # 确保从左向右绘制（统一处理方向）
        if x0 > x1:
            # 交换起点和终点
            x0, x1 = x1, x0
            y0, y1 = y1, y0

        # 初始化中点算法的决策参数（初始误差值）
        d = 2 * dy - dx
        # 确定y的步进方向（根据起点和终点的相对位置）
        y_step = 1 if y0 < y1 else -1   # 1表示向上，-1表示向下

        # 主循环：从x0到x1逐步计算每个x对应的y值
        y = y0  # 初始化y值
        for x in range(x0, x1 + 1):# 包含终点x1
            # 根据斜率方向恢复坐标
            points.append((y, x) if steep else (x, y))

            # 更新决策参数
            if d >= 0:
                # 如果决策参数>=0，选择NE点（y增加/减少）
                y += y_step # 调整y坐标
                d -= 2 * dx # 更新决策参数
            d += 2 * dy

        # 批量绘制优化（将点列表展平为[x1,y1,x2,y2,...]格式）
        if len(points) > 1:# 至少需要2个点才能画线
            # 使用列表推导式展平坐标点
            flat_points = [coord for point in points for coord in point]
            # 调用画布的create_line方法绘制直线
            canvas.create_line(*flat_points,  ** kwargs)

    def resize(self, dx, dy):
        pass


"""
$矩形$
"""


class Rectangle(Shape):
    def __init__(self, points, ** kwargs):
        super().__init__(**kwargs)
        self.points = points    # [左上角, 右下角]
        # print(f"data[points][0] = {points[0]}, data[points][1] = {points[1]}")

    def to_dict(self):
        data = super().to_dict()
        data.update({
            "points": self.points  # [左上角, 右下角]
        })
        return data

    @classmethod
    def from_dict(cls, data):
        return cls(
            points=[data["points"][0], data["points"][1]],
            is_filled=data.get("is_filled", False),
            color=data["color"],
            line_width=data["line_width"],
            transform=data["transform"],
            fill=data.get("fill"),
            auto_fill=data.get("auto_fill", False)
        )

    def draw(self, canvas):
        # 应用仿射变换到坐标点
        p0 = self._apply_transform(self.points[0])
        p1 = self._apply_transform(self.points[1])

        # 计算动态缩放因子
        scale_x = self.transform_matrix[0]
        scale_y = self.transform_matrix[3]
        # scaled_width = abs(p1[0] - p0[0]) * scale_x
        # scaled_height = abs(p1[1] - p0[1]) * scale_y

        # 动态调整线宽
        scale_factor = max(scale_x, scale_y)
        width = int(self.line_width * scale_factor)

        if self.line_width <= 2:
            # 使用中点算法绘制四边
            if self.auto_fill or self.is_filled:
                self.is_filled = True
                self._draw_midpoint_rectangle(canvas, p0, p1, self.color,fill=self.get_fill_color())
            else:
                self._draw_midpoint_rectangle(canvas, p0, p1, self.color)
        else:
            # print("开始绘制矩形")
            # 使用Tkinter原生方法
            # print(f"Rectangle's auto_fill:{self.auto_fill}")
            if self.auto_fill or self.is_filled:
                self.is_filled = True
                # 先绘制填充层（无边框）
                canvas.create_rectangle(
                    p0[0], p0[1], p1[0], p1[1],
                    fill=self.get_fill_color(),
                    width=0  # 消除填充层的边框
                )

                # 绘制边框
            canvas.create_rectangle(
                p0[0], p0[1], p1[0], p1[1],
                outline=self.color,
                width=width
            )


        if self.selected:
            x1, y1, x2, y2 = self.get_bounding_box()
            canvas.create_rectangle(
                x1 - 5, y1 - 5, x2 + 5, y2 + 5,
                outline='red', dash=(4, 2)
            )
            # 绘制四角控制点
            handle_size = 8
            corners = [(x1, y1), (x2, y1), (x2, y2), (x1, y2)]
            for cx, cy in corners:
                canvas.create_oval(
                    cx - handle_size // 2, cy - handle_size // 2,
                    cx + handle_size // 2, cy + handle_size // 2,
                    fill='red'
                )

    def _draw_midpoint_rectangle(self, canvas, p0, p1, color,  ** kwargs):
        """中点算法绘制矩形四边，支持填充"""
        fill_color = kwargs.get('fill', '')
        outline_color = color

        # 计算矩形边界
        x_min = int(min(p0[0], p1[0]))
        x_max = int(max(p0[0], p1[0]))
        y_min = int(min(p0[1], p1[1]))
        y_max = int(max(p0[1], p1[1]))

        # 填充逻辑
        if fill_color:
            # 验证颜色格式
            valid_fill = self._validate_color(fill_color)

            # 批量绘制水平线
            fill_lines = []
            for y_scan in range(y_min, y_max + 1):
                fill_lines.extend([x_min, y_scan, x_max, y_scan])

            canvas.create_line(*fill_lines,
                               fill=valid_fill,
                               width=0,  # 消除线间隔
                               tags="fill_area")

        # 绘制边界
        self.midpoint_line(canvas, x_min, y_min, x_max, y_min, color=outline_color)
        self.midpoint_line(canvas, x_max, y_min, x_max, y_max, color=outline_color)
        self.midpoint_line(canvas, x_max, y_max, x_min, y_max, color=outline_color)
        self.midpoint_line(canvas, x_min, y_max, x_min, y_min, color=outline_color)

    def contains(self, x, y):
        # 应用仿射变换到坐标点
        p0 = self._apply_transform(self.points[0])  # 左上角变换后坐标
        p1 = self._apply_transform(self.points[1])  # 右下角变换后坐标
        return (min(p0[0], p1[0]) <= x <= max(p0[0], p1[0])) and (min(p0[1], p1[1]) <= y <= max(p0[1], p1[1]))

    def resize(self, dx, dy):
        # 转换到局部坐标系（移除变换影响）
        inv_matrix = self._get_inverse_matrix()
        local_dx = dx * inv_matrix[0]
        local_dy = dy * inv_matrix[3]

        # 调整原始尺寸（非变换矩阵）
        self.points[1] = (
            self.points[1][0] + local_dx,
            self.points[1][1] + local_dy
        )

    @staticmethod
    def midpoint_line(canvas, x0, y0, x1, y1,  ** kwargs):
        """中点算法绘制直线"""
        dx = abs(x1 - x0)
        dy = abs(y1 - y0)
        steep = dy > dx
        color = kwargs.get('color', 'black')

        # 坐标轴交换处理
        if steep:
            x0, y0 = y0, x0
            x1, y1 = y1, x1

        # 确保从左到右绘制
        if x0 > x1:
            x0, x1 = x1, x0
            y0, y1 = y1, y0

        dx = x1 - x0
        dy = abs(y1 - y0)
        error = dx // 2
        y_step = 1 if y0 < y1 else -1
        y = y0

        # 生成像素点集
        points = []
        for x in range(int(x0), int(x1) + 1):
            coord = (y, x) if steep else (x, y)
            points.append(coord)
            error -= dy
            if error < 0:
                y += y_step
                error += dx

        # 批量绘制像素点
        for px, py in points:
            canvas.create_rectangle(
                px - 1, py - 1, px + 1, py + 1,
                outline=color, fill=color, width=0
            )

    def get_bounding_box(self):
        """获取变换后的包围盒"""
        p0 = self._apply_transform(self.points[0])
        p1 = self._apply_transform(self.points[1])
        return (min(p0[0], p1[0]), min(p0[1], p1[1]),
                max(p0[0], p1[0]), max(p0[1], p1[1]))


"""
多边形
"""

class Polygon(Shape):
    def __init__(self, points,polygon_type=None,  ** kwargs):
        """
        :param points: 顶点坐标列表，格式[(x1,y1), (x2,y2)...]
        """
        super().__init__(**kwargs)
        self.polygon_type = polygon_type
        self.points = points  # 存储原始局部坐标
        self.transform_matrix = kwargs.get("transform", [1, 0, 0, 1, 0, 0])

    # 在Polygon类中修改draw方法
    def draw(self, canvas):
        """动态创建多边形对象，无需维护ID"""
        # 应用变换并取整坐标
        transformed = [
            (int(self._apply_transform(p)[0]),
             int(self._apply_transform(p)[1]))
            for p in self.points
        ]

        # 统一设置填充属性
        fill_color = self.get_fill_color() if (self.auto_fill or self.is_filled) else ''
        # if self.polygon_type == "star":
        #     # print("draw-start")
        #     for i in range(len(self.points)):
        #         x, y = self.points[i]
        #         # canvas.create_oval(x - 2, y - 2, x + 2, y + 2, fill=self.color)  # 标记顶点
        #         if i > 0:
        #             canvas.create_line(self.points[i - 1][0], self.points[i - 1][1], x, y, fill=self.color)
        #     canvas.create_line(self.points[-1][0], self.points[-1][1], self.points[0][0], self.points[0][1], fill=self.color)  # 闭合路径
        #     return

        # 绘制多边形,创建多边形对象
        canvas.create_polygon(
            [coord for p in transformed for coord in p],
            outline=self.color,
            fill=fill_color,  # 自动处理填充状态
            width=self.line_width,
        )

    def _apply_transform(self, point):
        """正确应用仿射变换"""
        a, b, c, d, e, f = self.transform_matrix
        x = a * point[0] + c * point[1] + e  # X轴分量
        y = b * point[0] + d * point[1] + f  # Y轴分量
        return (x, y)


    def get_bounding_box(self):
        # 获取变换后坐标极值
        transformed = [self._apply_transform(p) for p in self.points]
        x_coords = [p[0] for p in transformed]
        y_coords = [p[1] for p in transformed]
        return (min(x_coords), min(y_coords),
                max(x_coords), max(y_coords))

    def contains(self, x, y):
        """射线法碰撞检测"""
        n = len(self.points)
        inside = False
        transformed = [self._apply_transform(p) for p in self.points]

        for i in range(n):
            p1 = transformed[i]
            p2 = transformed[(i + 1) % n]

            # 线段与水平射线相交检测
            if ((p1[1] > y) != (p2[1] > y)) and \
                    (x < (p2[0] - p1[0]) * (y - p1[1]) / (p2[1] - p1[1]) + p1[0]):
                inside = not inside

        return inside


    def rotate(self, angle_degrees):
        rad = math.radians(angle_degrees)  # 将角度转换为弧度
        cos_a = math.cos(rad)
        sin_a = math.sin(rad)
        new_matrix = [
            cos_a, -sin_a,  # X轴旋转分量
            sin_a, cos_a,  # Y轴旋转分量
            self.transform_matrix[4],  # 平移分量X
            self.transform_matrix[5]  # 平移分量Y
        ]
        self.transform_matrix = new_matrix

    def resize(self, dx, dy):
        """精准中心缩放算法"""
        # 1. 获取当前包围盒参数
        transformed = [self._apply_transform(p) for p in self.points]
        x_coords = [p[0] for p in transformed]
        y_coords = [p[1] for p in transformed]
        min_x, max_x = min(x_coords), max(x_coords)
        min_y, max_y = min(y_coords), max(y_coords)

        # 2. 计算当前中心点（屏幕坐标系）
        current_center = ((min_x + max_x) / 2, (min_y + max_y) / 2)

        # 3. 计算缩放因子
        base_width = max_x - min_x if (max_x - min_x) != 0 else 1
        base_height = max_y - min_y if (max_y - min_y) != 0 else 1
        scale_x = 1 + dx / base_width
        scale_y = 1 + dy / base_height

        # 4. 构建仿射变换矩阵
        a, b, c, d, e, f = self.transform_matrix
        new_matrix = [
            a * scale_x,
            b * scale_x,
            c * scale_y,
            d * scale_y,
            e + current_center[0] * (1 - scale_x),  # 关键修正项
            f + current_center[1] * (1 - scale_y)
        ]
        self.transform_matrix = new_matrix

    def to_dict(self):
        # print("I'm polygon")
        """增强版序列化方法"""
        data = super().to_dict()
        data.update({
            "version": "2.0",  # 新增版本标识
            "points": [[round(x, 4), round(y, 4)] for x, y in self.points],  # 精度优化
            "closed": True  # 标识多边形闭合特性
        })
        # 显式包含变换矩阵分量
        data["transform"] = [round(v, 6) for v in self.transform_matrix]
        return data

    @classmethod
    def from_dict(cls, data):
        """健壮版反序列化"""
        version = data.get("version", "1.0")

        # 版本迁移逻辑
        if version < "2.0":
            data.setdefault("closed", True)
            data["points"] = [tuple(p) for p in data["points"]]

        return cls(
            points=data["points"],
            is_filled=data.get("is_filled", data.get("auto_fill", False)),  # 兼容旧版字段
            color=data["color"],
            line_width=data["line_width"],
            transform=data.get("transform", [1, 0, 0, 1, 0, 0]),  # 默认单位矩阵
            fill=data.get("fill", data["color"]),  # 填充色回退逻辑
            auto_fill=data.get("auto_fill", False)
        )

class PolygonGenerator:
    # 修改PolygonGenerator的正多边形顶点计算
    @staticmethod
    def regular_polygon(center, sides, radius):
        """
        生成正多边形顶点坐标
        功能：计算指定中心点、边数和半径的正多边形顶点坐标

        :param center: 元组(x,y)，表示正多边形的中心点坐标
        :param sides: 整数，表示多边形的边数（至少为3）
        :param radius: 正数，表示外接圆半径
        :return: 顶点坐标列表[(x1,y1), (x2,y2), ...]
        :raises ValueError: 当参数不合法时抛出异常

        实现说明：
        1. 使用极坐标公式计算顶点位置
        2. 默认从12点钟方向开始顺时针生成顶点
        3. 自动适配屏幕坐标系（Y轴向下为正）
        """
        # 参数校验
        # if radius <= 0 or sides < 3:
        #     raise ValueError("Invalid polygon parameters")
        # 角度偏移量（从12点方向开始计算）
        angle_offset = math.pi / 2
        return [
            (
                center[0] + radius * math.cos(angle_offset + 2 * math.pi * i / sides),
                center[1] - radius * math.sin(angle_offset + 2 * math.pi * i / sides)  # Y轴取反适配屏幕坐标系
            )
            for i in range(sides)
        ]

    @staticmethod
    def star(center, points=5, outer_radius=50, inner_radius=25):
        """
        生成规则星形顶点（支持五角星及其他奇数顶点星形）
        实现原理：
        1. 使用极坐标公式计算顶点位置
        2. 交替使用内外半径生成星形尖角
        3. 自动适配屏幕坐标系（Y轴向下为正）
        :param center: (x,y)中心坐标
        :param points: 星型顶点数
        :param outer_radius: 外顶圆
        :param inner_radius: 内顶圆
        :return: 顶点坐标列表
        """
        if points < 5 or points % 2 == 0:
            raise ValueError("星形顶点数必须为≥5的奇数")
        vertices = []   # 存储所有顶点
        angle_step = 2 * math.pi / (points * 2)  # 正确角度步进
        angle_offset = math.pi / 2  # 从12点方向开始

        for i in range(points * 2):
            # 交替使用外半径和内半径（奇偶索引交替）
            radius = inner_radius if i % 2 else outer_radius
            # 计算当前角度（每次步进一个完整间隔）
            angle = angle_offset + angle_step * i
            # 计算顶点坐标（极坐标转直角坐标）
            x = center[0] + radius * math.cos(angle)
            y = center[1] - radius * math.sin(angle)  # Y轴取反适配屏幕坐标系
            vertices.append((x, y))
        return vertices

    @staticmethod
    def diamond(center, width=60, height=40):
        """
        生成标准菱形顶点坐标
        :param center: 中心点坐标 (x,y)
        :param width: 水平宽度
        :param height: 垂直高度
        :return: 四个顶点坐标列表
        """
        a = width / 2  # 计算水平半轴长
        b = height / 2  # 计算垂直半轴长

        points = [
            (center[0] + a, center[1]),  # 右顶点 (中心点右侧a单位)
            (center[0], center[1] + b),  # 上顶点 (中心点上方b单位)
            (center[0] - a, center[1]),  # 左顶点 (中心点左侧a单位)
            (center[0], center[1] - b)  # 下顶点 (中心点下方b单位)
        ]
        return points
    @staticmethod
    def parallelogram(center, base_vector=(60, 0), height=40, skew_angle=30):
        """
        生成斜四边形顶点（平行四边形）
        :param center: 中心点坐标 (cx, cy)
        :param base_vector: 基底向量 (dx, dy) 控制长度和水平倾斜
        :param height: 垂直高度
        :param skew_angle: 侧边倾斜角度（度）
        """
        import math

        # 计算基底向量分量
        dx, dy = base_vector
        base_length = math.hypot(dx, dy)

        # 计算侧边偏移量
        rad = math.radians(skew_angle)
        skew_x = height * math.sin(rad)
        skew_y = height * math.cos(rad)

        # 计算四个顶点（相对于中心点对称分布）
        points = [
            # 左下顶点 (中心点偏移基底和高度方向)
            (center[0] - dx / 2 - skew_x / 2, center[1] - dy / 2 + skew_y / 2),
            # 右下顶点 (保持基底长度，应用高度偏移)
            (center[0] + dx / 2 - skew_x / 2, center[1] + dy / 2 + skew_y / 2),
            # 右上顶点 (应用全部偏移)
            (center[0] + dx / 2 + skew_x / 2, center[1] + dy / 2 - skew_y / 2),
            # 左上顶点 (仅应用高度偏移)
            (center[0] - dx / 2 + skew_x / 2, center[1] - dy / 2 - skew_y / 2)
        ]
        return points

    @staticmethod
    def regular_hexagon(center=(0, 0), side_length=50):
        """生成正六边形顶点坐标
        :param center: 中心点坐标 (cx, cy)
        :param side_length: 边长（等效于外接圆半径）
        """
        angle_offset = math.pi / 2  # 从12点方向开
        points = [
            (
                center[0] + side_length * math.cos(angle_offset + 2 * math.pi * i / 6),
                center[1] - side_length * math.sin(angle_offset + 2 * math.pi * i / 6)  # Y轴取反适配屏幕坐标系
            )
            for i in range(6)
        ]
        return points

    @staticmethod
    def line_hexagon(center=(0, 0), width=100, height=80, cut_size=0.1):
        """
        生成切角六边形顶点（上下对称切角）
        :param center: 中心点坐标 (cx, cy)
        :param width: 基底宽度
        :param height: 总高度
        :param cut_size: 切角尺寸（基底宽度比例）
        :return: 六个顶点坐标列表
        """
        # 计算矩形边界
        rect_left = center[0] - width / 2  # 左边界
        rect_top = center[1] - height / 2  # 上边界
        cut_length = width * cut_size  # 实际切角长度

        return [
            (rect_left + cut_length, rect_top),  # 顶部左切点
            (rect_left + width - cut_length, rect_top),  # 顶部右切点
            (rect_left + width, center[1]),  # 右侧中点
            (rect_left + width - cut_length, rect_top + height),  # 底部右切点
            (rect_left + cut_length, rect_top + height),  # 底部左切点
            (rect_left, center[1])  # 左侧中点
        ]


    @staticmethod
    def lower_cut_hexagon(center=(0, 0), width=200, height=100, cut_size=0.2):
        """生成下切角六边形顶点（类似流程图循环下界符号）
        :param center: 中心点坐标 (cx, cy)
        :param width: 基底宽度（完整矩形宽度）
        :param height: 总高度（切角后垂直高度）
        :param cut_ratio: 切割比例（占基底宽度的比例，推荐0.1-0.3）
        """
        cut_length = width * cut_size
        left = center[0] - width / 2
        top = center[1] - height / 2

        return [
            (left, top),  # 左上角点
            (left + width, top),  # 右上角点
            (left + width, center[1]),  # 右侧中点
            (left + width - cut_length, top + height),  # 底部右切点
            (left + cut_length, top + height),  # 底部左切点
            (left, center[1])  # 左侧中点
        ]

    @staticmethod
    def upper_cut_hexagon(center=(0, 0), width=200, height=100, cut_size=0.2):
        """生成上切角六边形顶点（类似流程图循环上界符号）
        :param center: 中心点坐标 (cx, cy)
        :param width: 基底宽度（完整矩形宽度）
        :param height: 总高度（切角后垂直高度）
        :param cut_size: 切割比例（占基底宽度的比例，推荐0.1-0.3）
        """
        # 参数有效性验证
        if not isinstance(center, (tuple, list)) or len(center) != 2:
            raise ValueError("中心点必须是包含两个数值的元组/列表")
        if cut_size <= 0 or cut_size >= 0.5:
            raise ValueError("切角比例必须在0到0.5之间")

        cut_length = width * cut_size
        left = center[0] - width / 2
        top = center[1] - height / 2

        return [
            (left + cut_length, top),  # 顶部左切点
            (left + width - cut_length, top),  # 顶部右切点
            (left + width, center[1]),  # 右侧中点
            (left + width, top + height),  # 右下角点（保持完整）
            (left, top + height),  # 左下角点（保持完整）
            (left, center[1]),  # 左侧中点
            (left + cut_length, top)  # 闭合路径
        ]

    @staticmethod
    def generate_track(center=(0, 0), straight_length=84.39, radius=36.5, lane_width=1.22):
        """
        生成标准跑道形状顶点坐标
        :param center: 中心点坐标 (x,y)
        :param straight_length: 直道长度
        :param radius: 弯道半径
        :param lane_width: 跑道宽度
        :return: 跑道轮廓顶点坐标列表
        """
        import numpy as np

        cx, cy = center
        points = []

        # 计算左右半圆的圆心位置
        left_center = (cx - straight_length / 2, cy)  # 左半圆圆心
        right_center = (cx + straight_length / 2, cy)  # 右半圆圆心

        # 生成左半圆（下半圆，顺时针绘制）
        for angle in np.linspace(90, 270, 60):  # 从顶部开始绘制180度圆弧
            rad = np.deg2rad(angle)  # 角度转弧度
            x = left_center[0] + radius * np.cos(rad)
            y = left_center[1] + radius * np.sin(rad)
            points.append((x, y))

        # 连接下直道（从右到左）
        points.append((cx - straight_length / 2, cy - radius))  # 左下端点
        points.append((cx + straight_length / 2, cy - radius))  # 右下端点

        # 生成右半圆（上半圆，顺时针绘制）
        for angle in np.linspace(270, 450, 60):  # 从底部开始绘制180度圆弧
            rad = np.deg2rad(angle % 360)  # 角度归一化
            x = right_center[0] + radius * np.cos(rad)
            y = right_center[1] + radius * np.sin(rad)
            points.append((x, y))

        # 连接上直道（从左到右）
        points.append((cx + straight_length / 2, cy + radius))  # 右上端点
        points.append((cx - straight_length / 2, cy + radius))  # 左上端点

        # 闭合路径（回到起点）
        points.append(points[0])
        return points
"""
$椭圆$
"""


class Ellipse(Shape):
    def __init__(self, center_x, center_y, major_axis, minor_axis,  ** kwargs):
        """
        :param center_x: 椭圆中心X坐标
        :param center_y: 椭圆中心Y坐标
        :param major_axis: 长轴长度
        :param minor_axis: 短轴长度
        """
        super().__init__(**kwargs)
        self.center = (center_x, center_y)
        self.major_axis = major_axis  # 长轴半径
        self.minor_axis = minor_axis  # 短轴半径

    def to_dict(self):
        data = super().to_dict()
        data.update({
            "center": self.center,
            "major_axis": self.major_axis,
            "minor_axis": self.minor_axis
        })
        return data

    @classmethod
    def from_dict(cls, data):
        return cls(
            data["center"][0],
            data["center"][1],
            data["major_axis"],
            data["minor_axis"],
            is_filled=data.get("is_filled", False),
            color=data["color"],
            line_width=data["line_width"],
            transform=data["transform"],
            fill=data.get("fill"),
            auto_fill=data.get("auto_fill", False)
        )

    def draw(self, canvas):
        # 应用仿射变换到中心点
        x, y = self._apply_transform(self.center)

        # 计算动态缩放因子
        scale_x = self.transform_matrix[0]
        scale_y = self.transform_matrix[3]
        scaled_major = self.major_axis * scale_x
        scaled_minor = self.minor_axis * scale_y

        # 动态调整线宽
        if self.line_width <= 2:
            if self.auto_fill or self.is_filled:
                self.is_filled = True
                self.midpoint_ellipse(
                    canvas, x, y, scaled_major, scaled_minor,
                    color=self.color,
                    fill=self.get_fill_color(),
                    line_width=self.line_width)
            else:
                self.midpoint_ellipse(
                    canvas, x, y, scaled_major, scaled_minor,
                    color=self.color,
                    line_width=self.line_width)
        else:
            # 使用Tkinter原生方法
            if self.auto_fill or self.is_filled:
                self.is_filled = True
                canvas.create_oval(
                    x - scaled_major, y - scaled_minor,
                    x + scaled_major, y + scaled_minor,
                    outline=self.color,
                    fill=self.get_fill_color(),
                    width=int(self.line_width * max(scale_x, scale_y))  # 线宽同步缩放
                )

            canvas.create_oval(
                x - scaled_major, y - scaled_minor,
                x + scaled_major, y + scaled_minor,
                outline=self.color,
                width=int(self.line_width * max(scale_x, scale_y))  # 线宽同步缩放
            )
        if self.selected:
            x1, y1, x2, y2 = self.get_bounding_box()
            canvas.create_oval(
                x1 - 5, y1 - 5, x2 + 5, y2 + 5,
                outline='red', dash=(4, 2)
            )
            # 在长轴端点添加控制点
            handle_size = 8
            canvas.create_oval(
                x2 - handle_size // 2, (y1 + y2) // 2 - handle_size // 2,
                x2 + handle_size // 2, (y1 + y2) // 2 + handle_size // 2,
                fill='red'
            )

    def get_bounding_box(self):
        """获取变换后的包围盒"""
        x, y = self._apply_transform(self.center)
        return (x - self.major_axis, y - self.minor_axis,
                x + self.major_axis, y + self.minor_axis)

    def contains(self, x, y):
        if self.major_axis == 0 or self.minor_axis == 0:
            return False  # 无效椭圆不响应点击
        # 转换到局部坐标系
        local_x = (x - self.transform_matrix[4]) / self.transform_matrix[0]
        local_y = (y - self.transform_matrix[5]) / self.transform_matrix[3]
        dx = (local_x - self.center[0]) / self.major_axis
        dy = (local_y - self.center[1]) / self.minor_axis
        return dx ** 2 + dy ** 2 <= 1.0

    def resize(self, dx, dy):
        """通过变换矩阵调整椭圆尺寸"""
        # 获取当前变换后的包围盒
        x1, y1, x2, y2 = self.get_bounding_box()

        # 计算新的尺寸（假设拖拽右下角控制点）
        new_width = x2 + dx - x1
        new_height = y2 + dy - y1

        # 更新椭圆参数（长轴和短轴）
        self.major_axis = new_width / 2
        self.minor_axis = new_height / 2

        # 或者通过变换矩阵实现缩放
        scale_x = 1 + dx / (x2 - x1)
        scale_y = 1 + dy / (y2 - y1)
        self.transform_matrix[0] *= scale_x  # X轴缩放
        self.transform_matrix[3] *= scale_y  # Y轴缩放

    @staticmethod
    def midpoint_ellipse(canvas, cx, cy, a, b,  ** kwargs):
        """中点椭圆算法（支持填充优化）"""
        fill_color = kwargs.get('fill', '')
        outline_color = kwargs.get('color', 'black')

        points = set()
        x, y = 0, b
        a_sq, b_sq = a  **  2, b  **  2
        d = b_sq - a_sq * b + 0.25 * a_sq

        # ===== 区域1绘制：斜率绝对值 < 1 =====
        while a_sq * (2 * y - 1) > 2 * b_sq * (x + 1):
            sym_points = [
                (cx + x, cy + y), (cx - x, cy + y),
                (cx + x, cy - y), (cx - x, cy - y)  # 仅保留四象限对称点
            ]
            points.update(sym_points)
            if d < 0:
                d += b_sq * (2 * x + 3)
            else:
                d += b_sq * (2 * x + 3) + 2 * a_sq * (-y + 1)
                y -= 1
            x += 1

        # ===== 区域2绘制：斜率绝对值 >= 1 =====
        d = b_sq * (x + 0.5) ** 2 + a_sq * (y - 1) ** 2 - a_sq * b_sq
        while y >= 0:
            sym_points = [
                (cx + x, cy + y), (cx - x, cy + y),
                (cx + x, cy - y), (cx - x, cy - y)  # 仅保留四象限对称点
            ]
            points.update(sym_points)
            if d > 0:
                d += a_sq * (3 - 2 * y)
            else:
                d += a_sq * (3 - 2 * y) + 2 * b_sq * (x + 1)
                x += 1
            y -= 1

        # ===== 填充逻辑=====
        if fill_color:
            scan_lines = {}
            # 收集每行最小/最大x坐标
            for px, py in points:
                if py not in scan_lines:
                    scan_lines[py] = {'min_x': px, 'max_x': px}
                else:
                    scan_lines[py]['min_x'] = min(scan_lines[py]['min_x'], px)
                    scan_lines[py]['max_x'] = max(scan_lines[py]['max_x'], px)

            # 批量生成水平填充线）
            fill_lines = []
            for y_scan in scan_lines:
                x_start = scan_lines[y_scan]['min_x']
                x_end = scan_lines[y_scan]['max_x']
                fill_lines.extend([x_start, y_scan, x_end, y_scan])

            if fill_lines:
                canvas.create_line(*fill_lines,
                                   fill=fill_color,
                                   width=0,  # 消除线间隔）
                                   tag="fill_area")

        # ===== 绘制边界点 =====
        for px, py in points:
            canvas.create_rectangle(
                px - 1, py - 1, px + 1, py + 1,
                outline=outline_color,
                fill=outline_color,  # 保持边界颜色一致
                width=0
            )



"""
画笔
"""


class Brush(Shape):
    def __init__(self, points=None,  ** kwargs):
        super().__init__(**kwargs)
        self.points = points if points else []
        self.smoothness = 0.5  # 平滑系数

    def draw(self, canvas):
        """实现平滑线条绘制"""
        if len(self.points) < 2: return

        # 应用坐标变换
        transformed_points = [self._apply_transform(p) for p in self.points]

        # 生成平滑路径（使用Chaikin算法）
        smooth_points = self._chaikin_smooth(transformed_points)

        # 绘制带样条曲线的路径
        canvas.create_line(
            *[coord for p in smooth_points for coord in p],
            smooth=True,
            splinesteps=16,
            fill=self.color,
            width=self.line_width
        )

    def get_bounding_box(self):
        """计算包围盒"""
        if not self.points: return (0, 0, 0, 0)
        x_coords = [p[0] for p in self.points]
        y_coords = [p[1] for p in self.points]
        return (min(x_coords), min(y_coords), max(x_coords), max(y_coords))

    def add_point(self, x, y):
        """动态添加坐标点"""
        self.points.append((x, y))

    def _apply_transform(self, point):
        """完整仿射变换公式"""
        x = point[0] * self.transform_matrix[0] + \
            point[1] * self.transform_matrix[1] + \
            self.transform_matrix[4]
        y = point[0] * self.transform_matrix[2] + \
            point[1] * self.transform_matrix[3] + \
            self.transform_matrix[5]
        return (x, y)

    def _chaikin_smooth(self, points):
        """优化后的平滑算法"""
        if len(points) < 4: return points
        smoothed = []
        # 首尾点保持原位置
        smoothed.append(points[0])
        # 中间点插值
        for i in range(1, len(points) - 1):
            p_prev = points[i - 1]
            p_curr = points[i]
            p_next = points[i + 1]
            smoothed.append((
                (p_prev[0] + 6 * p_curr[0] + p_next[0]) / 8,
                (p_prev[1] + 6 * p_curr[1] + p_next[1]) / 8
            ))
        smoothed.append(points[-1])
        return smoothed



    def contains(self, x, y):
        """精确碰撞检测（考虑线宽和路径形状）"""
        if not self.points:
            return False

        # 计算变换后的实际线宽
        transformed_line_width = self.line_width * max(self.transform_matrix[0], self.transform_matrix[3])
        tolerance = transformed_line_width / 2 + 2  # 包含抗锯齿区域

        # 快速包围盒检测
        x_min, y_min, x_max, y_max = self.get_bounding_box()
        if not (x_min - tolerance <= x <= x_max + tolerance and
                y_min - tolerance <= y <= y_max + tolerance):
            return False

        # 转换所有点到画布坐标系
        transformed_points = [self._apply_transform(p) for p in self.points]

        # 遍历所有相邻点构成的线段
        for i in range(len(transformed_points) - 1):
            x1, y1 = transformed_points[i]
            x2, y2 = transformed_points[i + 1]

            # 计算点到线段的最短距离
            distance = self._distance_to_segment((x, y), (x1, y1), (x2, y2))
            if distance <= tolerance:
                return True

        return False

    def _distance_to_segment(self, point, start, end):
        """计算点到线段的最短距离"""
        px, py = point
        x1, y1 = start
        x2, y2 = end

        dx = x2 - x1
        dy = y2 - y1
        segment_length_sq = dx ** 2 + dy ** 2

        if segment_length_sq == 0:
            return math.hypot(px - x1, py - y1)

        t = ((px - x1) * dx + (py - y1) * dy) / segment_length_sq
        t = max(0, min(1, t))

        nearest_x = x1 + t * dx
        nearest_y = y1 + t * dy

        return math.hypot(px - nearest_x, py - nearest_y)

    @classmethod
    def from_dict(cls, data):
        """反序列化方法"""
        return cls(
            points=data["points"],
            color=data["color"],
            line_width=data["line_width"],
            transform=data.get("transform")
        )

    def to_dict(self):
        transformed_points = [self._apply_transform(p) for p in self.points]
        return {
            **  super().to_dict(),
            "type": "Brush",
            "points": transformed_points,  # 保存变换后坐标
            "transform": self.transform_matrix  # 明确保存变换参数
        }
